package com.jacky.iot;

import com.jacky.iot.resources.DtlsObservableResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.config.CoapConfig;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.config.SystemConfig;
import org.eclipse.californium.elements.config.TcpConfig;
import org.eclipse.californium.elements.config.UdpConfig;
import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.util.concurrent.TimeUnit;

public class CoapDtlsServer/* extends CoapServer*/ {

    static {
        CoapConfig.register();
        UdpConfig.register();
        DtlsConfig.register();
        TcpConfig.register();
        System.setProperty("COAP_ROOT_RESOURCE_NOTE",
                "Note: the data sent to this server is public visible to other\n" +
                        "      users! Don't send data, which requires data privacy.");
    }

    private static final File CONFIG_FILE = new File("CaliforniumPlugtest3.properties");
    private static final String CONFIG_HEADER = "Californium CoAP Properties file for AppServer Server";

    private static final int DEFAULT_MAX_RESOURCE_SIZE = 8192;
    private static final int DEFAULT_BLOCK_SIZE = 64;

    private static CoapServer coapServer;

    /**
     * Special configuration defaults handler.
     */
    private static final Configuration.DefinitionsProvider DEFAULTS = new Configuration.DefinitionsProvider() {

        @Override
        public void applyDefinitions(Configuration config) {
            config.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, 6);
            config.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, false);
        }
    };

    public CoapDtlsServer(Configuration config) {
//       继承CoapServer方式,调用父类构造方法使用监听模式生效
//        super(config);
//        add(new DtlsObservableResource("obs"));
    }

    public static void main(String[] args) throws Exception {
        // 1. 继承CoapServer方式,此方式需要调用父类的的构造方法,不然对于监听模式的资源是不会生效的
//        Configuration config = initConfig();
//        coapServer = new CoapDtlsServer(config);
//        coapServer.addEndpoint(initDtlsEndpoint());

        coapServer.add(new DtlsObservableResource("obs"));
        coapServer.start();
    }

    public static Configuration initConfig() throws GeneralSecurityException, IOException {
        Configuration configuration = Configuration.createWithFile(CONFIG_FILE, CONFIG_HEADER, new Configuration.DefinitionsProvider() {
            @Override
            public void applyDefinitions(Configuration config) {
                config.set(SystemConfig.HEALTH_STATUS_INTERVAL, 300, TimeUnit.SECONDS);
                config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
                config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
                config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
                config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_COUNT, 4);
                config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_TIME, 30, TimeUnit.SECONDS);
                config.set(CoapConfig.TCP_NUMBER_OF_BULK_BLOCKS, 1);
                config.set(CoapConfig.MAX_ACTIVE_PEERS, 10000);
                config.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, false);
                config.set(DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT, null, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, 6);
//            config.set(DtlsConfig.DTLS_PRESELECTED_CIPHER_SUITES, PRESELECTED_CIPHER_SUITES);
                config.set(DtlsConfig.DTLS_MAX_CONNECTIONS, 10000);
                config.set(DtlsConfig.DTLS_REMOVE_STALE_DOUBLE_PRINCIPALS, false);
                config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_QUIET_TIME, 4, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_THRESHOLD, 8);
                config.set(DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT, 3, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_ADDITIONAL_ECC_TIMEOUT, 8, TimeUnit.SECONDS);
                config.set(TcpConfig.TCP_CONNECT_TIMEOUT, 15, TimeUnit.SECONDS);
                config.set(TcpConfig.TCP_CONNECTION_IDLE_TIMEOUT, 60, TimeUnit.MINUTES);
                config.set(TcpConfig.TLS_HANDSHAKE_TIMEOUT, 60, TimeUnit.SECONDS);
            }
        });
        coapServer = new CoapServer(configuration);
        Endpoint dtlsEndpoint = initDtlsEndpoint();
        coapServer.addEndpoint(dtlsEndpoint);
        return configuration;
    }

    private static Endpoint initDtlsEndpoint() throws GeneralSecurityException, IOException {
        SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials(
                SslContextUtil.CLASSPATH_SCHEME + "keystore.jks", "server", "keyPass".toCharArray(),
                "serverPass".toCharArray());
        Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates(
                SslContextUtil.CLASSPATH_SCHEME + "trustStore.jks", "rootca", "trustPass".toCharArray());

        Configuration configuration = Configuration.createWithFile(Configuration.DEFAULT_FILE, "DTLS server", DEFAULTS);
        DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(configuration);
        builder.setAddress(new InetSocketAddress(5684));
        builder.setCertificateIdentityProvider(new SingleCertificateProvider(
                serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), CertificateType.RAW_PUBLIC_KEY,
                CertificateType.X_509
        )).setAdvancedCertificateVerifier(StaticNewAdvancedCertificateVerifier.builder()
                .setTrustedCertificates(trustedCertificates).setTrustAllRPKs().build());

        DTLSConnector dtlsConnector = new DTLSConnector(builder.build());

        CoapEndpoint endpoint = new CoapEndpoint.Builder().setConnector(dtlsConnector).build();
        return endpoint;
    }
}

